package com.bizmda.bizsip.common;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.bizmda.log.trace.MDCTraceUtils;

import java.util.Map;

/**
 * 平台内部标准报文的报文体。
 * <p>平台内部所有内部报文都是BizMessage格式，data是JSONObject格式。
 * @author 史正烨
 */
public class BizMessage<T> {
    private int code;
    private String message;
    private String extMessage;
    private String appServiceId;
    private String traceId;
    private String parentTraceId;
    private long timestamp;
    private T data;

    /**
     * 构造方法
     */
    public BizMessage() {
    }

    /**
     * 通过JSONObject来构造BizMessage
     * @param jsonObject 通过JSONObject构造的入参
     */
    public BizMessage(JSONObject jsonObject) {
        this.code = jsonObject.getInt("code");
        this.message = jsonObject.getStr("message");
        this.extMessage = jsonObject.getStr("extMessage");
        this.appServiceId = jsonObject.getStr("appServiceId");
        this.traceId = jsonObject.getStr("traceId");
        this.parentTraceId = jsonObject.getStr("parentTraceId");
        this.timestamp = jsonObject.getLong("timestamp");
        this.data = (T)jsonObject.get("data");
    }

    /**
     * 通过Map来构造BizMessage
     * @param map 通过Map构造的入参
     */
    public BizMessage(Map<String,Object> map) {
        this.code = (int)map.get("code");
        this.message = (String)map.get("message");
        this.extMessage = (String)map.get("extMessage");
        this.appServiceId = (String)map.get("appServiceId");
        this.traceId = (String)map.get("traceId");
        this.parentTraceId = (String)map.get("parentTraceId");
        this.timestamp = (long) map.get("timestamp");
        Object o = map.get("data");
        if (o instanceof Map) {
            this.data = (T)JSONUtil.parseObj(o);
        }
        else {
            this.data = (T)o;
        }
    }

    /**
     * 创建新交易的报文头
     * @param appServiceId 应用服务ID
     * @return 平台标准报文
     */
    public static BizMessage<JSONObject> createNewTransaction(String appServiceId) {
        BizMessage<JSONObject> bizMessage = new BizMessage<>();
        bizMessage.appServiceId = appServiceId;
        bizMessage.traceId = IdUtil.fastSimpleUUID();
        MDCTraceUtils.putTraceId(bizMessage.traceId);
        bizMessage.timestamp = System.currentTimeMillis();
        return bizMessage;
    }

    /**
     * 创建子交易的报文头
     * @param appServiceId 应用服务ID
     * @param parentBizMessage 父交易的报文
     * @return 平台标准报文
     */
    public static BizMessage<JSONObject> createChildTransaction(String appServiceId,BizMessage<JSONObject> parentBizMessage) {
        BizMessage<JSONObject> bizMessage = new BizMessage<>();
        BeanUtil.copyProperties(parentBizMessage,bizMessage);
        bizMessage.appServiceId = appServiceId;
        bizMessage.traceId = IdUtil.fastSimpleUUID();
        bizMessage.parentTraceId = parentBizMessage.traceId;
        bizMessage.timestamp = System.currentTimeMillis();
        return bizMessage;
    }

    /**
     * 构建一个成功返回的平台报文
     * @param <T> BizMessage泛型类型
     * @param inBizMessage 当前交易的原平台报文
     * @param data 返回的平台报文数据体
     * @return 平台标准报文
     */
    public static <T> BizMessage<T> buildSuccessMessage(BizMessage<?> inBizMessage, T data) {
        BizMessage<T> bizMessage = new BizMessage<>();

        BeanUtil.copyProperties(inBizMessage,bizMessage);

        bizMessage.setCode(0);
        bizMessage.setMessage("success");
        bizMessage.setExtMessage(null);
        bizMessage.setData(data);
        return bizMessage;
    }

    /**
     * 构建一个失败的平台报文
     * @param <T> BizMessage泛型类型
     * @param inBizMessage 当前交易的原平台报文
     * @param e 出错异常
     * @return 平台标准报文
     */
    public static <T> BizMessage<T> buildFailMessage(BizMessage<T> inBizMessage,Exception e) {
        BizMessage<T> bizMessage = new BizMessage<>();

        BeanUtil.copyProperties(inBizMessage,bizMessage);
        if (e instanceof BizException) {
            BizException bizException = (BizException)e;
            bizMessage.setCode(bizException.getCode());
            bizMessage.setMessage(bizException.getMessage());
            if (CharSequenceUtil.isEmpty(bizException.getExtMessage())) {
                bizMessage.setExtMessage(ExceptionUtil.stacktraceToString(e));
            }
            else {
                bizMessage.setExtMessage(bizException.getExtMessage());
            }
        }
        else {
            bizMessage.setCode(BizResultEnum.OTHER_ERROR.getCode());
            bizMessage.setMessage(e.getMessage());
            bizMessage.setExtMessage(ExceptionUtil.stacktraceToString(e));
        }
        bizMessage.data = null;
        return bizMessage;
    }

    /**
     * 获取返回码
     * @return 返回码
     */
    public int getCode() {
        return code;
    }

    /**
     * 设置返回码
     * @param code 返回码
     */
    public void setCode(int code) {
        this.code = code;
    }

    /**
     * 获取返回消息
     * @return 返回消息
     */
    public String getMessage() {
        return message;
    }

    /**
     * 设置返回消息
     * @param message 返回消息
     */
    public void setMessage(String message) {
        this.message = message;
    }

    /**
     * 获取返回附加消息
     * @return 返回附加消息
     */
    public String getExtMessage() {
        return extMessage;
    }

    /**
     * 设置返回附加消息
     * @param extMessage 返回附加消息
     */
    public void setExtMessage(String extMessage) {
        this.extMessage = extMessage;
    }

    /**
     * 获取应用服务ID
     * @return 应用服务ID
     */
    public String getAppServiceId() {
        return appServiceId;
    }

    /**
     * 设置应用服务ID
     * @param appServiceId 应用服务ID
     */
    public void setAppServiceId(String appServiceId) {
        this.appServiceId = appServiceId;
    }

    /**
     * 获取交易跟踪号
     * @return 交易跟踪号
     */
    public String getTraceId() {
        return traceId;
    }

    /**
     * 设置交易跟踪号
     * @param traceId 交易跟踪号
     */
    public void setTraceId(String traceId) {
        this.traceId = traceId;
    }

    /**
     * 获取父交易跟踪号
     * @return 父交易跟踪号
     */
    public String getParentTraceId() {
        return parentTraceId;
    }

    /**
     * 设置父交易跟踪号
     * @param parentTraceId 父交易跟踪号
     */
    public void setParentTraceId(String parentTraceId) {
        this.parentTraceId = parentTraceId;
    }

    /**
     * 获取交易开始时间
     * @return 交易开始时间
     */
    public long getTimestamp() {
        return timestamp;
    }

    /**
     * 设置交易开始时间
     * @param timestamp 交易开始时间
     */
    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }

    /**
     * 获取消息数据
     * @return 消息数据
     */
    public T getData() {
        return data;
    }

    /**
     * 设置消息数据
     * @param data 消息数据
     */
    public void setData(T data) {
        this.data = data;
    }
}
